/*
 *
 *   Copyright (C) 2005 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <string.h>
#include <stdlib.h>
#include <urlglib/root_node.h>

static void root_node_add_active (RootNode* rnode, CategoryNode* cnode)
{
	category_node_ref (cnode);
	rnode->active_list = g_list_append (rnode->active_list, cnode);
}

static void root_node_remove_active (RootNode* rnode, CategoryNode* cnode, GList* link)
{
	rnode->active_list = g_list_remove_link (rnode->active_list, link);
	g_list_free_1 (link);
	category_node_unref (cnode);
}

void root_node_finalize (RootNode* rnode)
{
	GList*        list;

	for (list=rnode->active_list; list; list=list->next)
		category_node_unref (CATEGORY_NODE (list->data));
	g_list_free (rnode->active_list);

	category_node_unref (rnode->category_default);
	base_node_instance_finalize (BASE_NODE (rnode));
}

RootNode* root_node_new ()
{
	RootNode*  rnode = g_malloc0 (sizeof (RootNode));

	base_node_instance_init (BASE_NODE (rnode));
	rnode->finalize = (BaseNodeFinalizeFunc)root_node_finalize;

	rnode->active_max = 3;
	rnode->category_default = category_node_new_without_queue ();
	return rnode;
}

void root_node_refresh (RootNode* rnode)
{
	GList*        list;
	GList*        list_next;
	CategoryNode* cnode;

	// refresh
	for (list=rnode->active_list; list; list=list_next) {
		list_next = list->next;
		cnode = list->data;

		// if cnode removed by user
		if (base_node_parent (BASE_NODE (cnode)) == NULL) {
			category_node_stop (cnode);
			root_node_remove_active (rnode, cnode, list);
			continue;
		}

		category_node_refresh (cnode);

		// if cnode stop.
		if (category_node_is_active (cnode) == FALSE)
			root_node_remove_active (rnode, cnode, list);
	}
}

void root_node_start_paused (RootNode* rnode)
{
	CategoryNode* cnode;
	QueueNode*    qnode;
	DownloadNode* dnode;

	cnode = root_node_first_category (rnode);

	while (cnode) {
		qnode = category_node_waiting_queue (cnode);
		dnode = queue_node_first_download (qnode);
		while (dnode) {
			if (dnode->state == DOWNLOAD_STATE_STOP)
				download_node_start (dnode);
			dnode = download_node_next (dnode);
		}
		cnode = category_node_next (cnode);
	}
}

int  root_node_start (RootNode* rnode)
{
	CategoryNode* cnode;
	GList*        link;
	int           nth_node, nth_node_beg = -1;

	// start download in it's category
	for (link=rnode->active_list; link; link=link->next) {
		cnode = link->data;
		if (category_node_n_active_download (cnode) < cnode->active_max)
			category_node_start (cnode);
	}

	// decide starting position.
	link = g_list_last (rnode->active_list);
	if (link) {
		cnode = link->data;
		nth_node_beg = root_node_category_position (rnode, cnode);
		nth_node = nth_node_beg + 1;
	}
	if (nth_node_beg == -1) {
		nth_node_beg = 0;
		nth_node = 0;
	}

	// start queuing
//	g_message ("starting nth category %d", nth_node_beg);
	do {
//		g_message ("current nth category %d", nth_node);
		if (g_list_length (rnode->active_list) >= rnode->active_max)
			break;
		cnode = root_node_nth_category (rnode, nth_node);
		if (cnode == NULL) {
//			g_message ("not found. nth category reset to %d", 0);
			nth_node = 0;
			continue;
		} else if (category_node_is_active (cnode) == FALSE) {
//			g_message ("found. nth category %d", nth_node);
			if (category_node_start (cnode))
				root_node_add_active (rnode, cnode);
		}
		nth_node++;
	} while (nth_node != nth_node_beg);

	return root_node_is_active (rnode);
}

void root_node_stop (RootNode* rnode)
{
	GList*        list;

	for (list=rnode->active_list; list; list=list->next)
		category_node_stop (CATEGORY_NODE (list->data));
}

// config writer & parser --------------------------------------------

void root_node_write_conf (RootNode* rnode, ConfWriter* cw)
{
	CategoryNode* cnode;

	conf_writer_start_element (cw, ROOT_NODE_TAG " active_max=\"%d\"", rnode->active_max);

	conf_writer_start_element (cw, "category_default");
	category_node_write_conf (rnode->category_default, cw);
	conf_writer_end_element (cw, "category_default");

	cnode = root_node_last_category (rnode);
	while (cnode) {
		category_node_write_conf (cnode, cw);
		cnode = (CategoryNode*) base_node_prev (BASE_NODE (cnode));
	}

	conf_writer_end_element (cw, ROOT_NODE_TAG);
}

void root_node_parser_start_element (GMarkupParseContext* gmpc,
                                     const gchar*    element_name,
                                     const gchar**   attr_names,
                                     const gchar**   attr_values,
                                     gpointer        cparser,
                                     GError**        error)
{
	RootNode*     rnode = CONF_PARSER_DATA (cparser);
	CategoryNode* cnode;
	int  ii;

	if (strcmp (element_name, ROOT_NODE_TAG)==0) {
		for (ii=0; attr_names[ii]; ii++) {
			if (strcmp (attr_names[ii], "active_max")==0)
				rnode->active_max = atoi (attr_values[ii]);
		}
	} else if (strcmp (element_name, CATEGORY_NODE_TAG)==0) {
		cnode = category_node_new ();
		root_node_prepend (rnode, cnode);
		conf_parser_push (cparser, &category_node_parser, cnode);
		category_node_parser_attr (attr_names, attr_values, cnode);
	} else if (strcmp (element_name, "category_default")==0) {
		cnode = rnode->category_default;
		conf_parser_push (cparser, &category_node_parser, cnode);
	}
}

void root_node_parser_end_element (GMarkupParseContext*  gmpc,
                                   const gchar*      element_name,
                                   gpointer          cparser,
                                   GError**          error)
{
	if (strcmp (element_name, ROOT_NODE_TAG)==0)
		conf_parser_pop (cparser);
}

void root_node_parser_attr (const gchar**     attr_names,
                            const gchar**     attr_values,
                            RootNode*         rnode)
{
}

const GMarkupParser root_node_parser = {
	root_node_parser_start_element,
	root_node_parser_end_element,
	NULL,
	NULL,
	NULL
};

